package com.bx.implatform.service.impl;

import com.bx.implatform.config.props.MinioProperties;
import com.bx.implatform.contant.Constant;
import com.bx.implatform.enums.FileType;
import com.bx.implatform.enums.ResultCode;
import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.service.FileService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.thirdparty.MinioService;
import com.bx.implatform.util.FileUtil;
import com.bx.implatform.util.ImageUtil;
import com.bx.implatform.vo.UploadImageVO;
import com.bx.implatform.vo.UploadVideoVO;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 文件上传服务
 *
 * @author: Blue
 * @date: 2024-09-28
 * @version: 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {

    private final MinioService minioSerivce;

    private final MinioProperties minioProps;

    @PostConstruct
    public void init() {
        if (!minioSerivce.bucketExists(minioProps.getBucketName())) {
            // 创建bucket
            minioSerivce.makeBucket(minioProps.getBucketName());
            // 公开bucket
            minioSerivce.setBucketPublic(minioProps.getBucketName());
        }
    }

    @Override
    public String uploadFile(MultipartFile file) {
        Long userId = SessionContext.getSession().getUserId();
        // 大小校验
        if (file.getSize() > Constant.MAX_FILE_SIZE) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件大小不能超过20M");
        }
        // 上传
        String fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getFilePath(), file);
        if (StringUtils.isEmpty(fileName)) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件上传失败");
        }
        String url = generUrl(FileType.FILE, fileName);
        log.info("文件文件成功，用户id:{},url:{}", userId, url);
        return url;
    }

    @Override
    public UploadImageVO uploadImage(MultipartFile file) {
        try {
            // 大小校验
            if (file.getSize() > Constant.MAX_IMAGE_SIZE) {
                throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片大小不能超过20M");
            }
            // 图片格式校验
            if (!FileUtil.isImage(file.getOriginalFilename())) {
                throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法");
            }
            // 上传原图
            UploadImageVO vo = new UploadImageVO();
            String fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getImagePath(), file);
            if (StringUtils.isEmpty(fileName)) {
                throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
            }
            vo.setOriginUrl(generUrl(FileType.IMAGE, fileName));
            // 大于30K的文件需上传缩略图
            if (file.getSize() > 30 * 1024) {
                byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30);
                fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getImagePath(),
                    file.getOriginalFilename(), imageByte, file.getContentType());
                if (StringUtils.isEmpty(fileName)) {
                    throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
                }
            }
            vo.setThumbUrl(generUrl(FileType.IMAGE, fileName));
            log.info("文件图片成功，url:{}", vo.getOriginUrl());
            return vo;
        } catch (IOException e) {
            log.error("上传图片失败，{}", e.getMessage(), e);
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
        }
    }

    @Override
    public UploadVideoVO uploadVideo(MultipartFile file) {
        Long userId = SessionContext.getSession().getUserId();
        // 大小校验
        if (file.getSize() > Constant.MAX_VIDEO_SIZE) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "视频文件大小不能超过50M");
        }
        // 上传
        String fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getVideoPath(), file);
        if (StringUtils.isEmpty(fileName)) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "视频上传失败");
        }
        String url = generUrl(FileType.VIDEO, fileName);
        String coverName = createCoverImage(file);
        log.info("上传视频成功，用户id:{},url:{}", userId, url);
        UploadVideoVO vo = new UploadVideoVO();
        vo.setVideoUrl(url);
        vo.setCoverUrl(generUrl(FileType.VIDEO, coverName));
        return vo;
    }

    private String createCoverImage(MultipartFile file) {
        try {
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(file.getInputStream());
            grabber.start();
            // 读取第一帧
            Java2DFrameConverter converter = new Java2DFrameConverter();
            BufferedImage firstFrame = converter.convert(grabber.grabImage());
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(firstFrame, "PNG", outputStream);
            // 压缩
            byte[] imageByte = ImageUtil.compressForScale(outputStream.toByteArray(), 150);
            String fileName = FileUtil.excludeExtension(file.getOriginalFilename()) + ".png";
            String imageName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getVideoPath(), fileName,
                imageByte, "image/png");
            grabber.stop();
            outputStream.close();
            return imageName;
        } catch (IOException e) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "视频上传失败");
        }
    }

    private String generUrl(FileType fileTypeEnum, String fileName) {
        String url = minioProps.getDomain() + "/" + minioProps.getBucketName();
        switch (fileTypeEnum) {
            case FILE:
                url += "/" + minioProps.getFilePath() + "/";
                break;
            case IMAGE:
                url += "/" + minioProps.getImagePath() + "/";
                break;
            case VIDEO:
                url += "/" + minioProps.getVideoPath() + "/";
                break;
            default:
                break;
        }
        url += fileName;
        return url;
    }

}
